package com.atguigu.gulimail.search.Service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.common.es.SkuEsModel;
import com.atguigu.gulimail.search.Service.MallSearchService;
import com.atguigu.gulimail.search.Vo.SearchParam;
import com.atguigu.gulimail.search.Vo.SearchResult;
import com.atguigu.gulimail.search.constant.EsConstant;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.swing.text.Highlighter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
public class MallSearchServiceImpl implements MallSearchService {

    @Autowired
    private RestHighLevelClient client;

    @Override
    public SearchResult serarch(SearchParam searchParam) {

        SearchResult result=null;

        //准备检索请求

        SearchRequest searchRequest=buildSearchRequest(searchParam);

        try{
            //执行请求
            SearchResponse response=client.search(searchRequest, RequestOptions.DEFAULT);
            //分析相应数据，封装；
            result=buildSearchResult(response,searchParam);
        }catch (IOException e){
            e.printStackTrace();
        }
        return result;
    }


    /**
     * 准备请求语句
     *  模糊匹配，过滤，（按照属性，分类，品牌，价格区间，库存），排序，分页，高亮，聚合分析
     * @return
     */
    public SearchRequest buildSearchRequest(SearchParam searchParam){

        SearchRequest request = new SearchRequest();
        SearchSourceBuilder searchSourceBuilder=new SearchSourceBuilder();//构建DSL语句的

//        模糊匹配，过滤，（按照属性，分类，品牌，价格区间，库存）
        //1.构建bool - query
        BoolQueryBuilder builder= QueryBuilders.boolQuery();
        //1.1 must-模糊匹配
        if(StringUtils.isNotEmpty(searchParam.getKeyword())){
            builder.must(QueryBuilders.matchQuery("skuTitle",searchParam.getKeyword()));
        }
        //1.2 bool - filter
        //1.2.1 按照三级分类进行筛选
        if (null!=searchParam.getCatalog3Id()){
            builder.filter(QueryBuilders.termQuery("catalogId",searchParam.getCatalog3Id()));
        }
        //1.2.2 按照品牌Id进行筛选
        if(null!=searchParam.getBrandId()){
            builder.filter(QueryBuilders.termsQuery("brandId",searchParam.getBrandId()));
        }
        //TODO 1.2.3 按照属性进行筛选
        if(searchParam.getAttrs()!=null && searchParam.getAttrs().size()>0){
            for (String attrsStr: searchParam.getAttrs()) {

                BoolQueryBuilder nestedboolQuery=QueryBuilders.boolQuery();

                // attrs= 1_5寸:8寸&attrs=2_16G:8G
                String[] s=attrsStr.split("_");
                String attrId=s[0];//检索属性的Id
                String[] attrValues=s[1].split(":");//属性检索的值

                nestedboolQuery.must(QueryBuilders.termQuery("attrs.attrId",attrId));
                nestedboolQuery.must(QueryBuilders.termsQuery("attrs.attrValue",attrValues));

                //每一个属性都得生成一个Nested嵌入式查询
                NestedQueryBuilder nestedQueryBuilder=QueryBuilders.nestedQuery("attrs",nestedboolQuery,ScoreMode.None);

                builder.filter(nestedQueryBuilder);
            }
        }
        //1.2.4 按照是否有库存进行筛选
        if(null!=searchParam.getHasStock()){
            builder.filter(QueryBuilders.termQuery("hasStock",searchParam.getHasStock()));
        }
        //1.2.5 按照价格区间进行查询
        if (StringUtils.isNotEmpty(searchParam.getSkuPrice())){
            String[] prices =searchParam.getSkuPrice().split("_");

            RangeQueryBuilder rangeQueryBuilder=QueryBuilders.rangeQuery("skuPrice");
            if(prices.length==1){
                String price_1=searchParam.getSkuPrice().split("_")[0];

                //只有最小值
                rangeQueryBuilder.gte(price_1);

            }
            if( prices.length==2){
                String price_1=searchParam.getSkuPrice().split("_")[0];
                String price_2=searchParam.getSkuPrice().split("_")[1];

                //只有最大值
                if(price_1.equals("")){
                    rangeQueryBuilder.lte(price_2);
                }else{
                    //两个值都有
                    rangeQueryBuilder.gte(price_1).lte(price_2);
                }
            }
            builder.filter(rangeQueryBuilder);
        }

        //将所有的查询条件进行封装
        searchSourceBuilder.query(builder);

//        排序，分页，高亮
        if(StringUtils.isNotEmpty(searchParam.getSort())){
            String sorts=searchParam.getSort();
            //sorts=hotScore_asc/desc;
            String[] s=sorts.split("_");
            String attr=s[0];
            String mode=s[1];

            if(mode.equalsIgnoreCase("asc")){
                searchSourceBuilder.sort(attr, SortOrder.ASC);
            }else {
                searchSourceBuilder.sort(attr, SortOrder.DESC);
            }
        }
        //分页    pageSize=5;  前端传递页码pageNum
        if(null!=searchParam.getPageNum()){
            searchSourceBuilder.from((searchParam.getPageNum()-1)*EsConstant.PRODUCT_PAGE_SIZE);
            searchSourceBuilder.size(EsConstant.PRODUCT_PAGE_SIZE);
        }else {
            searchSourceBuilder.from(0);
            searchSourceBuilder.size(EsConstant.PRODUCT_PAGE_SIZE);
        }



        //高亮显示
        if(StringUtils.isNotEmpty(searchParam.getKeyword())){
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("skuTitle");
            highlightBuilder.preTags("<b style='color:red'>");
            highlightBuilder.postTags("</b>");

            searchSourceBuilder.highlighter(highlightBuilder);
        }
//        聚合分析

        //聚合一： 品牌
        TermsAggregationBuilder brand_agg=AggregationBuilders.terms("brand_agg");
        brand_agg.field("brandId").size(50);
        brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1));
        brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1));
        searchSourceBuilder.aggregation(brand_agg);

        //聚合二： 品牌
        TermsAggregationBuilder cata_agg = AggregationBuilders.terms("catalog_agg").field("catalogId").size(20);
        cata_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1));
        searchSourceBuilder.aggregation(cata_agg);

        //聚合三： 属性聚合
            //聚合出所有的attrid
        NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");
        TermsAggregationBuilder attr_id_agg=AggregationBuilders.terms("attr_id_agg").field("attrs.attrId");
                //聚合出每个attr对应的name和value
        TermsAggregationBuilder attr_name_agg=AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1);
        TermsAggregationBuilder attr_value_agg=AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50);
        attr_id_agg.subAggregation(attr_name_agg);
        attr_id_agg.subAggregation(attr_value_agg);
        attr_agg.subAggregation(attr_id_agg);
        searchSourceBuilder.aggregation(attr_agg);

        String s = searchSourceBuilder.toString();
        System.out.println(s);
        request=new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX},searchSourceBuilder);


        return request;

    }

    /**
     * 封装返回结果
     * @param response
     * @return
     */
    public SearchResult buildSearchResult(SearchResponse response,SearchParam searchParam){

        SearchResult result=new SearchResult();
        SearchHits hits = response.getHits();

        //设置所有商品信息 sku
        if(hits.getHits()!=null){
            List<SkuEsModel> list=new ArrayList<>();
            for (SearchHit hit:hits.getHits()){
                String sourceAsString =hit.getSourceAsString();
                SkuEsModel esModel= JSON.parseObject(sourceAsString,SkuEsModel.class);
                if(StringUtils.isNotEmpty(searchParam.getKeyword())){
                    HighlightField skuTitle = hit.getHighlightFields().get("skuTitle");
                    String string = skuTitle.getFragments()[0].string();
                    esModel.setSkuTitle(string);

                }
                list.add(esModel);
            }
            result.setProducts(list);
        }

        //设置response涉及到到的所有属性信息
        List<SearchResult.AttrVo> attrVos=new ArrayList<>();
        ParsedNested attr_agg=response.getAggregations().get("attr_agg");
        ParsedLongTerms attrIdAgg = attr_agg.getAggregations().get("attr_id_agg");
        for(Terms.Bucket bucket:attrIdAgg.getBuckets()){
            SearchResult.AttrVo attrVo = new SearchResult.AttrVo();

            //属性id
            long attrId=bucket.getKeyAsNumber().longValue();
            attrVo.setAttrId(attrId);

            //属性name
            String attrNameAgg = ((ParsedStringTerms) bucket.getAggregations().get("attr_name_agg")).getBuckets().get(0).getKeyAsString();
            attrVo.setAttrName(attrNameAgg);

            //属性值
            List<String> collect = ((ParsedStringTerms) bucket.getAggregations().get("attr_value_agg")).getBuckets().stream().map(i -> {
                return ((Terms.Bucket) i).getKeyAsString();
            }).collect(Collectors.toList());
            attrVo.setAttrValue(collect);

            attrVos.add(attrVo);
        }

        result.setAttrVos(attrVos);
        //设置response涉及到的所有品牌信息
        List<SearchResult.BrandVo> brandVos=new ArrayList<>();
        ParsedLongTerms brand_agg=response.getAggregations().get("brand_agg");
        for (Terms.Bucket bucket:brand_agg.getBuckets()){
            SearchResult.BrandVo brandVo=new SearchResult.BrandVo();
            //品牌Id
            long brandId=bucket.getKeyAsNumber().longValue();
            brandVo.setBrandId(brandId);
            //品牌名
            String brandNameAgg = ((ParsedStringTerms) bucket.getAggregations().get("brand_name_agg")).getBuckets().get(0).getKeyAsString();
            brandVo.setBrandName(brandNameAgg);
            //品牌img
            String brandImgAgg = ((ParsedStringTerms) bucket.getAggregations().get("brand_img_agg")).getBuckets().get(0).getKeyAsString();
            brandVo.setBrandImg(brandImgAgg);
            brandVos.add(brandVo);
        }
        result.setBrandVos(brandVos);

        //当前所有商品涉及到的所有的分类信息
        ParsedLongTerms catalog_agg=response.getAggregations().get("catalog_agg");
        List<SearchResult.CatelogVo> catelogVos=new ArrayList<>();
        List<? extends Terms.Bucket> buckets=catalog_agg.getBuckets();
        for (Terms.Bucket bucket:buckets){
            SearchResult.CatelogVo catelogVo=new SearchResult.CatelogVo();
            //得到分类Id
            String keyAsString=bucket.getKeyAsString();
            catelogVo.setCatelogId(Long.valueOf(keyAsString));
                //分类名字的聚合
            ParsedStringTerms catalogNameAgg = bucket.getAggregations().get("catalog_name_agg");
            String cata_name = catalogNameAgg.getBuckets().get(0).getKeyAsString();
            catelogVo.setCatelogName(cata_name);

            catelogVos.add(catelogVo);
        }
        result.setCatelogVos(catelogVos);

        //分页信息 - 页码
        if(searchParam.getPageNum()!=null){
            result.setPageNum(searchParam.getPageNum());
        }else{
            result.setPageNum(1);
        }
        //分页总记录数
        long total=hits.getTotalHits().value;
        result.setTotal(total);
        //分页信息-总页码  总记录数/页size + 是否有余数
        Integer totalpage=(int)total%EsConstant.PRODUCT_PAGE_SIZE==0?(int)total/EsConstant.PRODUCT_PAGE_SIZE:(int)total/EsConstant.PRODUCT_PAGE_SIZE+1;
        result.setTotalPage( totalpage);

        List<Integer> pageNavs=new ArrayList<>();
        for (int i=1;i<=totalpage;i++){
            pageNavs.add(i);
        }
        result.setPageNavs(pageNavs);


        //TODO 构建面包屑导航功能
        List<SearchResult.NavVo> navVos=new ArrayList<>();



        return result;
    }

}
